package com.mask.im.business.service.impl;

import com.alibaba.fastjson.JSON;
import com.mask.im.business.common.model.RestResponse;
import com.mask.im.business.enums.MaskCallStatusEnum;
import com.mask.im.business.enums.MessageType;
import com.mask.im.business.model.entity.chat.IMFriendChatMessageDO;
import com.mask.im.business.model.vo.CallDetailVO;
import com.mask.im.business.service.ChannelService;
import com.mask.im.business.service.IMCallService;
import com.mask.im.sdk.dto.IMPushMessage;
import com.mask.im.sdk.service.MessagePushService;
import com.mask.token.util.MaskSecurityUtil;
import lombok.extern.log4j.Log4j2;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import javax.annotation.Resource;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * @className： IMCallServiceImpl
 * @Description:
 * @Author： jian
 * @Date： 2025/10/11 08:53
 */
@Log4j2
@Service
public class IMCallServiceImpl implements IMCallService {

    @Resource
    private ChannelService channelService;

    @Resource
    private MessagePushService pushService;

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // 用户当前通话
    private static final String USER_ACTIVE_CALL_PREFIX = "user:active:call:friend:";
    // 通话详情
    private static final String CALL_DETAIL_PREFIX = "call:detail:friend:";
    private static final String CALL_LOCK_PREFIX = "lock:call:friend:";

    // 活跃通话过期时间
    private static final long ACTIVE_CALL_EXPIRE_MINUTES = 30;
    //已结束通话保留时间
    private static final long ENDED_CALL_EXPIRE_HOURS = 24;


    @Override
    public RestResponse<?> requestCall(Long receiverId, Integer type, String offer) {
        Integer messageType = type == 0 ? MessageType.VOICE_CALL.getCode() : MessageType.VIDEO_CALL.getCode();
        Long userId = MaskSecurityUtil.getCurrentUserId();
        Integer retryCount = 0;
        //获取redis锁
        String lockKey = generateLockKey(userId, receiverId);
        // RLock lock = redissonClient.getLock(lockKey);
        try {
//            //校验锁
//            boolean tryLock = lock.tryLock(5, 30, TimeUnit.SECONDS);
//            if (!tryLock) {
//                return RestResponse.fail();
//            }

            //校验用户是否在线
            Boolean online = pushService.isOnline(receiverId);
            if (!online) {
                IMFriendChatMessageDO friendChatMessage = getImFriendChatMessageDO(receiverId, userId, messageType);
                IMPushMessage<IMFriendChatMessageDO> pushMessage = new IMPushMessage<>();
                pushMessage.setPushType("FRIEND");
                pushMessage.setSenderId(userId);
                pushMessage.setReceiverId(receiverId);
                pushMessage.setContent(friendChatMessage);
                pushService.pushMessage(pushMessage);
                //给自己推送消息
                pushMessage.setReceiverId(userId);
                pushService.pushMessage(pushMessage);
                return RestResponse.ok();
            }

            // 校验自己是否在通话中
            if (isUserInCall(userId)) {
                //不保存聊天记录,直接返回错误
                return RestResponse.fail("您当前正在通话中!");
            }
            //校验对方是否在通话中
            if (isUserInCall(receiverId)) {
                //保存聊天记录,并推送到对方
                IMFriendChatMessageDO friendChatMessage = getImFriendChatMessageDO(receiverId, userId, messageType);
                IMPushMessage<IMFriendChatMessageDO> pushMessage = new IMPushMessage<>();
                pushMessage.setPushType("FRIEND");
                pushMessage.setSenderId(userId);
                pushMessage.setReceiverId(receiverId);
                pushMessage.setContent(friendChatMessage);
                pushService.pushMessage(pushMessage);
                //给自己推送消息
                pushMessage.setReceiverId(userId);
                pushService.pushMessage(pushMessage);
                return RestResponse.ok();
            }
            // 记录用户当前通话
            setCalling(userId);
            setCalling(receiverId);

            //设置通话详情
            CallDetailVO callDetailVO = new CallDetailVO();
            callDetailVO.setCallerId(userId);
            callDetailVO.setAccepterId(receiverId);
            callDetailVO.setCallType(messageType);
            callDetailVO.setStatus(MaskCallStatusEnum.PENDING.name());
            String key = generateCallKey(userId, receiverId);
            setCallDetail(key, callDetailVO);

            // 给对方推送消息
            IMFriendChatMessageDO friendChatMessage = getImFriendChatMessageDO(receiverId, userId, messageType == 4 ? 98 : 99);
            friendChatMessage.setContent(offer);
            IMPushMessage<IMFriendChatMessageDO> pushMessage = new IMPushMessage<>();
            pushMessage.setPushType("FRIEND");
            pushMessage.setSenderId(userId);
            pushMessage.setReceiverId(receiverId);
            pushMessage.setContent(friendChatMessage);
            pushService.pushMessage(pushMessage);
            return RestResponse.ok();
        } catch (Exception e) {
            log.error("发起通话发生异常:" + e.getMessage(), e);
            delCalling(userId);
            delCalling(receiverId);
            if (e.getMessage().contains("WRONGTYPE")) {
                // 清理后立即重试，可能太快
                cleanupInvalidLockKey(lockKey);
                retryCount++;
                // 缺少删除验证和适当等待
            }
            return RestResponse.fail();
        } finally {
//            if (lock.isHeldByCurrentThread()) {
//                lock.unlock();
//            }
        }
    }

    private void cleanupInvalidLockKey(String lockKey) {
        try {
            log.info("尝试清理无效的锁键: {}", lockKey);
            Boolean deleted = redisTemplate.delete(lockKey);
            if (Boolean.TRUE.equals(deleted)) {
                log.info("成功清理锁键: {}", lockKey);
                // 添加短暂延迟确保Redis更新
                Thread.sleep(50);
            } else {
                log.warn("锁键清理失败或键不存在: {}", lockKey);
            }
        } catch (Exception e) {
            log.error("清理锁键失败: {}", lockKey, e);
        }
    }

    @Override
    public RestResponse<?> acceptCall(Long sendId, String answer) {
        Long userId = MaskSecurityUtil.getCurrentUserId();
        try {
            //获取保存的通话记录,修改通话的状态
            CallDetailVO callDetail = getCallDetail(sendId, userId);

            if (null == callDetail) {
                //通话已经结束
                return RestResponse.fail("通话结束");
            }
            //校验通话状态是否正确
            if (!callDetail.getStatus().equals("PENDING")) {
                return RestResponse.fail("通话结束");
            }

            callDetail.setAccepterId(userId);
            callDetail.setStartTime(new Date().getTime());
            callDetail.setStatus("ACCEPTED");
            String key = generateCallKey(sendId, userId);
            //修改状态
            setCallDetail(key, callDetail);

            //像发起者推送接收通话的通知,messageType设置为13为接听电话,后续使用魔法值
            IMFriendChatMessageDO friendChatMessage = getImFriendChatMessageDO(userId, sendId, 100);
            friendChatMessage.setContent(answer);
            IMPushMessage<IMFriendChatMessageDO> pushMessage = new IMPushMessage<>();
            pushMessage.setPushType("FRIEND");
            pushMessage.setSenderId(userId);
            pushMessage.setReceiverId(sendId);
            pushMessage.setContent(friendChatMessage);
            log.info("接听电话成功推送:{}", JSON.toJSON(pushMessage));
            pushService.pushMessage(pushMessage);
            return RestResponse.ok();
        } catch (Exception e) {
            log.error("接听通话发生异常:" + e.getMessage(), e);
            return RestResponse.fail("接听通话异常");
        }
    }

    @Override
    public RestResponse<?> cancelCall(Long receiverId) {
        //获取当前通话
        Long userId = MaskSecurityUtil.getCurrentUserId();
        CallDetailVO callDetail = getCallDetail(userId, receiverId);
        //校验是否存在和状态是否正常
        if (null == callDetail || !callDetail.getStatus().equals("REQUESTED")) {
            return RestResponse.fail("通话无法取消");
        }
        //更新通话状态
        callDetail.setStatus("CANCELLED");
        String key = generateCallKey(receiverId, userId);
        setCallDetail(key, callDetail);
        //设置当前用户通话状态为空
        delCalling(userId);
        delCalling(receiverId);

        //保存聊天记录,并推送到对方电话已经取消
        IMFriendChatMessageDO friendChatMessage = getImFriendChatMessageDO(receiverId, userId, 103);
        IMPushMessage<IMFriendChatMessageDO> pushMessage = new IMPushMessage<>();
        pushMessage.setPushType("FRIEND");
        pushMessage.setSenderId(userId);
        pushMessage.setReceiverId(receiverId);
        pushMessage.setContent(friendChatMessage);
        pushService.pushMessage(pushMessage);
        //TODO 保存聊天记录
        return RestResponse.ok();
    }

    @Override
    public RestResponse<?> rejectCall(Long sendId) {
        //获取当前登录用户
        Long userId = MaskSecurityUtil.getCurrentUserId();
        //检查当前通话状态
        CallDetailVO callDetail = getCallDetail(sendId, userId);
        if (null != callDetail){
            if (!callDetail.getStatus().equals(MaskCallStatusEnum.PENDING.name())) {
                return RestResponse.fail("无法拒绝通话");
            }
            //更新通话状态
            callDetail.setStatus(MaskCallStatusEnum.REJECTED.name());
        }
        String key = generateCallKey(sendId, userId);
        setCallDetail(key, callDetail);
        //清除双方通话状态
        delCalling(userId);
        delCalling(sendId);
        //保存聊天记录并推送给发起人已拒绝消息
        IMFriendChatMessageDO friendChatMessage = getImFriendChatMessageDO(sendId, userId, 105);
        IMPushMessage<IMFriendChatMessageDO> pushMessage = new IMPushMessage<>();
        pushMessage.setPushType("FRIEND");
        pushMessage.setSenderId(sendId);
        pushMessage.setReceiverId(userId);
        pushMessage.setContent(friendChatMessage);
        pushService.pushMessage(pushMessage);
        //TODO 保存聊天记录
        return RestResponse.ok();
    }

    @Override
    public RestResponse<?> endCall(Long otherId) {
        //获取的登录id
        Long userId = MaskSecurityUtil.getCurrentUserId();
        CallDetailVO callDetail = getCallDetail(userId, otherId);
        if (!callDetail.getStatus().equals(MaskCallStatusEnum.ACCEPTED.name()) && !callDetail.getStatus().equals(MaskCallStatusEnum.IN_CALL.name())) {
            return RestResponse.fail("通话状态异常");
        }
        callDetail.setStatus(MaskCallStatusEnum.ENDED.name());
        callDetail.setEndTime(new Date().getTime());
        //计算通话时长
        long duration = (callDetail.getEndTime() - callDetail.getStartTime()) / 1000;
        callDetail.setDuration(duration);
        String key = generateCallKey(otherId, userId);
        setCallDetail(key, callDetail);
        //清除双方通话状态
        delCalling(userId);
        delCalling(otherId);

        //保存聊天记录并推送给发起人已拒绝消息
        IMFriendChatMessageDO friendChatMessage = getImFriendChatMessageDO(otherId, userId, 102);
        IMPushMessage<IMFriendChatMessageDO> pushMessage = new IMPushMessage<>();
        pushMessage.setPushType("FRIEND");
        pushMessage.setSenderId(userId);
        pushMessage.setReceiverId(otherId);
        pushMessage.setContent(friendChatMessage);
        pushService.pushMessage(pushMessage);
        //TODO 保存聊天记录
        return RestResponse.ok();
    }

    /**
     * 发送WebRTC信令
     */
    @Override
    public RestResponse<?> sendSignaling(Long otherId, String candidate) {
        Long userId = MaskSecurityUtil.getCurrentUserId();
        CallDetailVO callDetail = getCallDetail(otherId, userId);

        if (callDetail != null) {
            if (!callDetail.getStatus().equals(MaskCallStatusEnum.ACCEPTED.name()) && !callDetail.getStatus().equals(MaskCallStatusEnum.IN_CALL.name())) {
                return RestResponse.fail("通话状态异常");
            }
        }

        //保存聊天记录并推送消息
        IMFriendChatMessageDO friendChatMessage = getImFriendChatMessageDO(otherId, userId, 101);
        friendChatMessage.setContent(candidate);
        IMPushMessage<IMFriendChatMessageDO> pushMessage = new IMPushMessage<>();
        pushMessage.setPushType("FRIEND");
        pushMessage.setSenderId(userId);
        pushMessage.setReceiverId(otherId);
        pushMessage.setContent(friendChatMessage);
        Boolean success = pushService.pushMessage(pushMessage);
        // 首次信令发送后更新为通话中
//        if (success && callDetail.getStatus().equals(MaskCallStatusEnum.ACCEPTED.name())) {
//            callDetail.setStatus(MaskCallStatusEnum.IN_CALL.name());
//            String key = generateCallKey(otherId, userId);
//            setCallDetail(key, callDetail);
//        }
        return RestResponse.ok();
    }

    @Override
    public RestResponse<?> heartbeat(Long otherId) {
        Long userId = MaskSecurityUtil.getCurrentUserId();
        // 会话续命
        String key = generateCallKey(userId, otherId);
        redisTemplate.expire(key, 60, TimeUnit.SECONDS);
        // 用户状态续命
        redisTemplate.expire(USER_ACTIVE_CALL_PREFIX + userId, 60, TimeUnit.MINUTES);
        return RestResponse.ok();
    }

    /**
     * 检查用户是否正在通话中
     */
    public boolean isUserInCall(Long userId) {
        String userCallKey = USER_ACTIVE_CALL_PREFIX + userId;
        Boolean hasKey = redisTemplate.hasKey(userCallKey);
        return Boolean.TRUE.equals(hasKey);
    }

    /**
     * 获取通话详情
     */
    public CallDetailVO getCallDetail(Long userIdA, Long userIdB) {
        String callKey = generateCallKey(userIdA, userIdB);
        return (CallDetailVO) redisTemplate.opsForValue().get(callKey);
    }

    // 新增：生成锁的 key（独立前缀）
    private String generateLockKey(Long userIdA, Long userIdB) {
        String userKey = Long.compare(userIdA, userIdB) < 0 ? userIdA + "_" + userIdB : userIdB + "_" + userIdA;
        return CALL_LOCK_PREFIX + userKey; // 锁的 key 格式：lock:call:friend:xx_xx
    }

    public String generateCallKey(Long userIdA, Long userIdB) {
        String userKey = Long.compare(userIdA, userIdB) < 0 ? userIdA + "_" + userIdB : userIdB + "_" + userIdA;
        return CALL_DETAIL_PREFIX + userKey;
    }

    public void setCalling(Long userId) {
        redisTemplate.opsForValue().set(USER_ACTIVE_CALL_PREFIX + userId, userId,
                ACTIVE_CALL_EXPIRE_MINUTES, TimeUnit.MINUTES);
    }

    public void delCalling(Long userId) {
        redisTemplate.delete(USER_ACTIVE_CALL_PREFIX + userId);
    }

    public void setCallDetail(String key, CallDetailVO callDetail) {
        redisTemplate.opsForValue().set(key, callDetail, ACTIVE_CALL_EXPIRE_MINUTES, TimeUnit.MINUTES);
    }

    private IMFriendChatMessageDO getImFriendChatMessageDO(Long receiverId, Long userId, Integer messageType) {
        IMFriendChatMessageDO friendChatMessage = new IMFriendChatMessageDO();
        friendChatMessage.setSenderId(userId);
        friendChatMessage.setReceiverId(receiverId);
        friendChatMessage.setReceiverDeleted(Boolean.FALSE);
        friendChatMessage.setSenderDeleted(Boolean.FALSE);
        friendChatMessage.setMessageType(messageType);
        friendChatMessage.setCreateTime(new Date());
        friendChatMessage.setIsRead(Boolean.FALSE);
        friendChatMessage.setSendStatus(0);
        return friendChatMessage;
    }


    // 格式化时长（秒→分:秒）
    private String formatDuration(long seconds) {
        if (seconds < 60) {
            return seconds + "秒";
        } else {
            long minutes = seconds / 60;
            long secs = seconds % 60;
            return minutes + "分" + secs + "秒";
        }
    }
//    public void handle(IMReceiveMessage imReceiveMessage, Channel channel) {
//        Integer type = imReceiveMessage.getExtend();
//        String receiverId = imReceiveMessage.getReceiverId().toString();
//        System.err.println(type + ":" + receiverId);
//        switch (type) {
//            case 1:
//                request(channel, receiverId);
//                break;
//            case 2:
//                response(receiverId);
//                break;
//            case 3:
//                askOffer(receiverId);
//                break;
//            case 4:
//                getOffer(imReceiveMessage.getContent(), receiverId);
//                break;
//            case 5:
//                answer(imReceiveMessage.getContent(), receiverId);
//                break;
//            case 6:
//                candidate(imReceiveMessage.getContent(), receiverId);
//                break;
//            case 7:
//                hangUp(receiverId, channel);
//                break;
//            case 8:
//                closeVideo(receiverId, channel);
//        }
//    }
//
//    private void closeVideo(String receiverId, Channel channel) {
//        Channel userIdChannel = channelService.getUserIdChannel(receiverId);
//        IMSendMessage websocketDTO = new IMSendMessage();
//        websocketDTO.setCmd(30008);
//        websocketDTO.setMessage(8);
//        if (userIdChannel != null) {
//            websocketDTO.setData("对方已挂断语音通话");
//            userIdChannel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO)));
//        }
//        websocketDTO.setData("语音通话结束");
//        channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO)));
//
//    }
//
//    private void hangUp(String receiverId, Channel channel) {
//        Long userId = MaskSecurityUtil.getCurrentUserId();
//        Channel receiverChannel = channelService.getUserIdChannel(receiverId);
//        IMSendMessage websocketDTO = new IMSendMessage();
//        websocketDTO.setCmd(30008);
//        websocketDTO.setMessage(7);
//        if (receiverChannel != null) {
//            websocketDTO.setData("对方已挂断语音通话");
//            receiverChannel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO)));
//        }
//        websocketDTO.setData("已挂断");
//        channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO)));
//
//        //TODO:测试插入语音聊天记录
//        //保存聊天记录
////        IMFriendChatMessageDO imFriendChatMessage = saveChatMessage(Long.valueOf(receiverId), "已取消", userId, 5, WebSocketConstant.READ, 1, new Date());
////        FriendChatMessageDTO friendChatMessageDTO = friendshipsService.getChatStorageDTO(imFriendChatMessage);
//        FriendChatMessageDTO friendChatMessageDTO = new FriendChatMessageDTO();
//        //封装消息
//        IMSendMessage websocketDTO1 = IMSendMessage.builder().message(friendChatMessageDTO).cmd(WebSocketConstant.SINGLE_CHAT).build();
//        //实时响应
//        if (Objects.nonNull(receiverChannel)) {
//            receiverChannel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO1)));
//        }
//        channel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO1)));
//        //异步刷新最新消息
////        changeRecentAsync(receiverChannel, Long.valueOf(receiverId), "[语音通话]", userId, 5, new Date());
//    }
//
//    private void candidate(Object content, String receiverId) {
//        Channel userIdChannel = channelService.getUserIdChannel(receiverId);
//        IMSendMessage websocketDTO = new IMSendMessage();
//        websocketDTO.setCmd(30008);
//        websocketDTO.setMessage(6);
//        websocketDTO.setData(content);
//        userIdChannel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO)));
//    }
//
//    private void answer(Object content, String receiverId) {
//        Channel userIdChannel = channelService.getUserIdChannel(receiverId);
//        IMSendMessage websocketDTO = new IMSendMessage();
//        websocketDTO.setCmd(30008);
//        websocketDTO.setMessage(5);
//        websocketDTO.setData(content);
//        userIdChannel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO)));
//    }
//
//    private void getOffer(Object offer, String receiverId) {
//        Channel userIdChannel = channelService.getUserIdChannel(receiverId);
//        IMSendMessage websocketDTO = new IMSendMessage();
//        websocketDTO.setCmd(30008);
//        websocketDTO.setMessage(4);
//        websocketDTO.setData(offer);
//        userIdChannel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO)));
//    }
//
//    private void askOffer(String receiverId) {
//        Channel userIdChannel = channelService.getUserIdChannel(receiverId);
//        IMSendMessage websocketDTO = new IMSendMessage();
//        websocketDTO.setCmd(30008);
//        websocketDTO.setMessage(3);
//        userIdChannel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO)));
//    }
//
//    private void response(String receiverId) {
//        Channel userIdChannel = channelService.getUserIdChannel(receiverId);
//        IMSendMessage websocketDTO = new IMSendMessage();
//        websocketDTO.setCmd(30008);
//        websocketDTO.setMessage(2);
//        HashMap<String, String> hashMap = new HashMap<>();
//        hashMap.put("from", MaskSecurityUtil.getCurrentUserId().toString());
//        websocketDTO.setData(hashMap);
//        userIdChannel.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(websocketDTO)));
//    }
}
